/*
 * plasticity.cpp
 *
 *  Created on: 11.04.2012
 *      Author: delgado
 */
#include "plasticity.h"

#include <algorithm>
using std::find;
#include <iostream>
using std::cout;
using std::endl;

namespace neurignacio{

Matrix clipped_Hebbian(const Pattern& Z, const Topology& W)
{
	Matrix J(W.n, W.n, 0); // Create a nxn Matrix filled with 0

	// clipped Hebbian Definition: Jij=Wij * Zp(i) * Zp(j)
	// Redefinition:
	// 	If there is a connection between neuron i and neuron j,
	// 	check if neuron i and neuron j are both in pattern p
	// ----------------------------
	// 1. Go through all the patterns
	for (Pattern::const_iterator p=Z.begin(); p!=Z.end(); ++p)
		// 2. Go through all connections of the topology
		for (Topology::const_iterator i=W.begin(); i!=W.end(); ++i)
		{
		// 3. Check if neuron 'i' is in pattern
		if (find(p->begin(), p->end(), i->first)!=p->end())
			// 4. If so, then check if any of the neurons connected to 'i' is in the pattern 'p' as well
			for (NeuronList::const_iterator j=i->second.begin(); j!=i->second.end(); ++j)
				if (find(p->begin(), p->end(), *j)!=p->end())
					// If so, Jij is equal to 1
					J(i->first,*j)=1;
		}
	return J;
}

void test_clipped(void)
{
	cout << "Running test_clipped" << endl;
	// Create Empty Topology of size 5
	// meaning network of 5 neurons without connections
	Topology W(5,0);
	// Create connections manually
	W[0].push_back(1);
	W[1].push_back(2);
	W[2].push_back(3);
	W[3].push_back(4);
	W[4].push_back(0);
	// Send W to stdout
	cout << "Topology W:" << endl;
	cout << W << endl;
	// Create Pattern Z
	Pattern Z(5,0.6);
	// Z0 should be (1,1,1,0,0)
	cout << "Z0: " << endl;
	cout << Z << endl;
	// Generate Patterns
	Z.generate(3);
	// send Z to stdout
	cout << "Pattern Z: " << endl;
	cout << Z << endl;
	// Create Hebbian Matrix
	Matrix J(5,5);
	J = clipped_Hebbian(Z,W);
	// send J to stdout
	cout << "Hebbian Matrix: " << endl;
	cout << J << endl;
	cout << "Done!" << endl;

}

void test_model()
{
	cout << "Running test_model" << endl;
	number_t nCells = 3000;
	real c = 0.5;
	real a = 0.1;

	Topology W(nCells, c);
	Pattern Z(nCells, a);
	Matrix J(nCells, nCells);
	J = clipped_Hebbian(Z,W);

	// iterate 5 x more
	vector<Matrix::data_t> I;	// Stores the inner product
	// Create X=Z0 vector for inner product
	vector<Matrix::data_t> X=Z.AsVector(0);

	for (int i=0; i<5; ++i)
	{
		I = inner(J.transpose(), X);
		vector<real> V(I.size());
		// Calculate Average 'spk_avg'
		real spk_avg=0;
		for (register size_t x=0; x<I.size(); ++x)
		{
			V[x]=static_cast<real>(I[x]) / nCells;
			spk_avg += V[x];
		}
		spk_avg /= V.size();
//		cout << "Average: " << spk_avg << endl;

		// Send some debug info to stdout
//		cout << "Before V=: " << endl;
//		for (vector<real>::iterator it=V.begin(); it!=V.end(); ++it)
//			cout << *it << " ";
//		cout << endl;
//		cout << " spk_avg=" << spk_avg << " * 0.433 = " << 0.433*spk_avg << endl;

		// Compute threshold
		real threshold = 0.433*spk_avg;
		size_t count=0;
		for (register size_t x=0; x<I.size(); ++x)
			if (V[x]>threshold)
			{
				X[x] = 1;
				++count;
			}
			else
				X[x]=0;
		cout << "Iteration " << i << "-> number of neurons over threshold: " << count << endl;
		// Print Result
//		cout << "After X=:" << endl;
//		for (vector<Matrix::data_t>::iterator it=X.begin(); it!=X.end(); ++it)
//			cout << (int)(*it) << " ";
//		cout << endl;
	}

	cout << "Done" << endl;

}
void example_clipped_Hebbian(void)
{
	cout << "Running test_clipped" << endl;
	// Create Empty Topology of size 5
	// meaning network of 5 neurons without connections
	Topology W(5,0);
	// Create connections manually
	W[0].push_back(1);
	W[1].push_back(2);
	W[2].push_back(3);
	W[3].push_back(4);
	W[4].push_back(0);
	// Send W to stdout
	cout << "Topology W:" << endl;
	cout << W << endl;
	// Create Pattern Z
	Pattern Z(5,0.6);
	// Z0 should be (1,1,1,0,0)
	cout << "Z0: " << endl;
	cout << Z << endl;
	// Generate Patterns
	// NeuroList contains a list with the active neurons (0..4)
	NeuronList Zp;
	// Z1
	Zp.push_back(0);
	Zp.push_back(2);
	Zp.push_back(3);
	Z.push_back(Zp);
	// Z2
	Zp.clear();
	Zp.push_back(1);
	Zp.push_back(1);
	Zp.push_back(3);
	Zp.push_back(4);
	Z.push_back(Zp);
	// Z3
	Zp.clear();
	Zp.push_back(0);
	Zp.push_back(1);
	Z.push_back(Zp);
	// send Z to stdout
	cout << "Pattern Z: " << endl;
	cout << Z << endl;
	// Create Hebbian Matrix
	Matrix J(5,5);
	J = clipped_Hebbian(Z,W);
	// send J to stdout
	cout << "Hebbian Matrix: " << endl;
	cout << J << endl;
	cout << "Transposed J:" << endl;
	cout << J.transpose() << endl;
	// Create X=Z0 vector for inner product
	vector<Matrix::data_t> X=Z.AsVector(0);
	vector<Matrix::data_t> I = inner(J.transpose(), X);
	cout << "Inner Product of J.transpose() with X=Z0:" << endl;
	for (vector<Matrix::data_t>::iterator i=I.begin(); i!=I.end(); ++i)
		cout << (int)*i << " ";
	cout << endl;

	cout << "Done!" << endl;
}


} // end namespace neurignacio
